Skip to content

feat: serve request_account and account requests from the AccountStore - #1808

Merged
bokelley merged 1 commit into
nextfrom
claude/account-requests-seller
Nov 2, 2026
Merged

bokelley merged 1 commit into
nextfrom
claude/account-requests-seller

Conversation

@bokelley

@bokelley bokelley commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Summary

This adds the seller side of request_account for Python, the counterpart of adcp-client#3586. The reporting-subscriptions DX work had deferred it. A decisioning AccountStore can now serve AdCP 3.3 account.requests, so a Python seller can serve request_account and list pending requests. A Python or TS buyer's client.accounts.ensure / change then routes to it.

class MyAccounts:
    def resolve(self, ref, auth_info=None): ...
    def list(self, filter=None, ctx=None): ...               # Account(..., revision=3)
    async def request_account(self, params, ctx=None): ...   # returns the wire AccountRequest
    async def list_requests(self, params, ctx=None):         # only when the buyer asks
        return AccountRequestsPage(items=[...], next_cursor=None)

request_account hook (AccountStoreRequestAccount)

  • PlatformHandler.request_account primes the caller's identity, as sync_accounts does, and calls the hook with a ResolveContext.
  • It serves a returned record as {"status": "completed", "request": ...}. A full response passes through.
  • Without the hook, the tool is not_supported and is not advertised.

Capability (gated to 3.3 and later)

  • A store with the hook advertises:
    • account.requests with supported: true, request_task: "request_account" and read_task: "list_accounts";
    • the account.requests experimental feature id.
  • The advisory lists (changeable_attributes, pending_action_kinds, event_type) come from the seller's declared DecisioningCapabilities.account.requests. The task names are always the framework's.
  • Callers before 3.3 see neither the block nor the feature id. An experimental_features claim made without the hook is dropped.

list_requests hook (AccountStoreListRequests)

  • list_accounts serves account_requests[] and account_requests_pagination (from AccountRequestsPage.next_cursor / total_count).
  • It runs only when the buyer sends include_requests, request_ids or requests_pagination, and only for 3.3 callers. The accounts page is unchanged.

No fabricated claims. The capabilities schema requires supported_billing in the account block. TS synthesizes { supported_billing: ['agent'] }; this PR does not. These configurations are refused at construction with ConfigurationError:

  • request_account without a declared account.supported_billing (or the legacy flat supported_billing);
  • list_requests without list;
  • a declared account.requests without the hook.

request_account without list_requests warns that buyers cannot poll a pending request, as TS does. Request-scoped capabilities (get_adcp_capabilities_for_request) that drop supported_billing while the hook is set raise the same error at request time.

Account.revision is served on list_accounts to 3.2 and later callers, as TS #3586 does. Buyers read it before a change.

request_account and list_requests join _ACCOUNT_STORE_METHODS, so PlatformRouter keeps them on the router's store. The accounts guide gains Serving request_account (sellers).

Deliberate differences from TS #3586

  • TS registers the hook on the runtime options (opts.accounts.requestAccount). Here it is a method on the AccountStore, next to list_requests, as the DX plan proposed (accounts.request).
  • Python has no adopter override object for the block. The optional lists come from the typed account.requests capability model, not a separate accountRequests option.

Test plan

tests/test_account_requests_seller.py:

  • The block and feature id go to 3.3 callers and are schema-valid. A 3.2 caller sees neither. A store without the hook advertises nothing, even with the feature claimed, and the tool answers not supported.
  • Each ConfigurationError case, and the warning.
  • request_account serves the hook's record (the response validates against RequestAccountResponse), with tool_name="request_account" on the context.
  • list_accounts checks:
    • it calls list_requests only when asked, and returns paging;
    • request_ids filters;
    • a 3.2 caller gets no requests;
    • revision is served from 3.2 and stripped for 3.1;
    • the response validates against ListAccountsResponse.
  • End to end:
    1. A Python ADCPClient reads the handler's capabilities, and ensure routes to request_account (not sync_accounts), giving AccountPending.
    2. After the seller approves, refresh() is AccountReady through list_accounts request_ids.
    3. A second ensure opens no duplicate request.
  • Router, handler-shim, account and decisioning suites. The drift tests now include the two new protocols.
  • ruff check and mypy src/adcp/decisioning/.

🤖 Generated with Claude Code

A decisioning AccountStore can now serve AdCP 3.3 account.requests, the
Python counterpart of adcp-client#3586:

- request_account on the store serves the tool, advertises it, and adds
  the account.requests capability block with supported, request_task
  and read_task, plus the account.requests experimental feature id.
  Optional lists come from the declared account.requests. Callers before
  3.3 see neither, and a feature claim without the hook is dropped.
- list_requests serves account_requests and account_requests_pagination
  on list_accounts when the buyer sends include_requests, request_ids or
  requests_pagination, for 3.3 callers only.
- The framework never invents billing claims: a store with
  request_account and no declared account.supported_billing is refused
  at construction with ConfigurationError, as are list_requests without
  list and a declared account.requests without the hook. request_account
  without list_requests warns that buyers cannot poll.
- Account.revision is served on list_accounts to 3.2 and later callers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean, additive seller-side request_account / account.requests support (AdCP 3.3), the counterpart to adcp-client#3586.

What I checked:

  • Semver signal: feat: is correct — new exports and two Protocol methods are additive optional surface; nothing removed, retyped, or flipped required↔optional on a public model. No breaking change shipped under a non-breaking prefix.
  • Version-translation pathway (DR-0032/0033): account.requests block + feature id gated to 3.3+; _project_account_requests (handler.py:2444-2467) drops both for sub-3.3 callers and when the hook is absent. Revision gated to 3.2+ in list_accounts (handler.py:3529-3533), and to_wire_account is reached only via _project_list_accounts so revision never leaks through sync_accounts. Droppable pathway, tested.
  • Config fail-closed: _validate_account_requests_config (handler.py:2452-2480) refuses request_account without supported_billing, list_requests without list, declared account.requests without a hook, warns on request_account without list_requests — each case tested.
  • No credentials in ctx_metadata, generated code untouched, import layering respected.

High-risk flag: true, but all six matching files are (modified) with no medium-or-higher findings — presumed safe per the modified-file rule. No deletions.

No blocking or medium findings. One non-blocking note (account_requests[] items bypass strip_credentials_from_wire_result — harmless today, schema has no secret fields; follow-up only if request records gain adopter passthrough).

No no-auto-approve team match, gated_paths false. Falls through to row 9.

@bokelley
bokelley merged commit e556c3b into next Nov 2, 2026
41 checks passed
@bokelley
bokelley deleted the claude/account-requests-seller branch November 2, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant